using System.Collections.Generic;

namespace Kopernikus.Parsing.Utility
{
    public class Combinator<T>
    {
        private readonly List<List<T>> m_alternatives = new List<List<T>>();

        public IEnumerable<List<T>> Combinations
        {
            get
            {
                if (m_alternatives.Count > 0)
                {
                    int[] currentAlternatives = new int[m_alternatives.Count];

                    while (true)
                    {
                        yield return AssembleCombination(currentAlternatives);
                        if (GenerateNextCombination(currentAlternatives))
                        {
                            break;
                        }
                    }
                }
            }
        }

        private bool GenerateNextCombination(int[] currentAlternatives)
        {
            if (currentAlternatives.Length == 0)
            {
                return true;
            }
            bool carry = false;
            for (int i = 0; i < currentAlternatives.Length; ++i)
            {
                if (++currentAlternatives[i] >= m_alternatives[i].Count)
                {
                    currentAlternatives[i] = 0;
                    carry = true;
                }
                else
                {
                    return false;
                }
            }
            return carry;
        }

        private List<T> AssembleCombination(int[] currentAlternatives)
        {
            List<T> combination = new List<T>(currentAlternatives.Length);
            for (int i = 0; i < currentAlternatives.Length; ++i)
            {
                combination.Add(m_alternatives[i][currentAlternatives[i]]);
            }
            return combination;
        }

        public void AddCombinations(params T[] alternatives)
        {
            AddCombinations((IEnumerable<T>) alternatives);
        }

        public void AddCombinations(IEnumerable<T> alternatives)
        {
            m_alternatives.Add(new List<T>(alternatives));
        }
    }
}